pretentious 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4eac89f08c915484d2749c73f703b6d8654f5e94
4
+ data.tar.gz: abf3901c3235fa2ee6d266cbb5cd019f22e83c2a
5
+ SHA512:
6
+ metadata.gz: a0f020ba8a6903e3bd55682c58e42a63c3ffddb3db7e99bada136aae4ecc2252a6ddd5a45ebe02f82e23f287aa8062f6a18b32d85a88c59af4558e782ac08d50
7
+ data.tar.gz: 4c4c7d237a30ec08800be2d09fa363f6ced13a2cfaea11651c41508b1950e499f7077d759a194c76f2262bfb784825c2605b20254d146e7ed5e1792843330357
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ .DS_Store
2
+ .idea
3
+ *.gem
4
+ /.bundle/
5
+ /.yardoc
6
+ /Gemfile.lock
7
+ /_yardoc/
8
+ /coverage/
9
+ /doc/
10
+ /pkg/
11
+ /spec/reports/
12
+ /tmp/
13
+ *.bundle
14
+ *.so
15
+ *.o
16
+ *.a
17
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pretentious.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Joseph Emmanuel Dayo
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # Ruby::Pretentious
2
+
3
+ Do you have a pretentious boss or dev lead that pushes you to embrace bdd/tdd but for reasons hate it or them?
4
+ here is a gem to deal with that. Now you CAN write code first and then GENERATE tests later!! Yes you heard that
5
+ right! This gem allows you to write your code first and then automatically generate tests using the code
6
+ you've written!
7
+
8
+ On a serious note, this gem allows you to generate tests template much better than those generated by default
9
+ for various frameworks.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'pretentious'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install pretentious
26
+
27
+ ## Usage
28
+
29
+ First Create an example file (etc. example.rb) and define the classes that you want to test, if the class is
30
+ already defined elsewhere just require them. Below is an example:
31
+
32
+ ```ruby
33
+ class Fibonacci
34
+
35
+ def fib(n)
36
+ return 0 if (n == 0)
37
+ return 1 if (n == 1)
38
+ return 1 if (n == 2)
39
+ return fib(n - 1) + fib(n - 2)
40
+ end
41
+
42
+ def self.say_hello
43
+ "hello"
44
+ end
45
+
46
+ end
47
+ ```
48
+
49
+ Inside a Pretentious.spec_for(...) block. Just write boring code that calls the methods of your class like
50
+ how you'd normally use them. Finally Specify the classes that you want to test:
51
+
52
+ ```ruby
53
+ class Fibonacci
54
+
55
+ def fib(n)
56
+ return 0 if (n == 0)
57
+ return 1 if (n == 1)
58
+ return 1 if (n == 2)
59
+ return fib(n - 1) + fib(n - 2)
60
+ end
61
+
62
+ def self.say_hello
63
+ "hello"
64
+ end
65
+
66
+ end
67
+
68
+ Pretentious.spec_for(Fibonacci) do
69
+
70
+
71
+ instance = Fibonacci.new
72
+
73
+ (1..10).each do |n|
74
+ instance.fib(n)
75
+ end
76
+
77
+ Fibonacci.say_hello
78
+
79
+ end
80
+ ```
81
+
82
+ Save your file and then switch to the terminal to invoke:
83
+
84
+ ddtgen example.rb
85
+
86
+ This will automatically generate rspec tests for Fibonacci under spec of the current working directory.
87
+
88
+ you can invoke spec at this point, but the tests will fail. Instead you should edit spec/spec_helper.rb and
89
+ put the necessary requires and definitions there.
90
+
91
+ For this example place the following into spec_helper.rb:
92
+
93
+ ```ruby
94
+ #inside spec_helper.rb
95
+
96
+ class Fibonacci
97
+
98
+ def fib(n)
99
+ return 0 if (n == 0)
100
+ return 1 if (n == 1)
101
+ return 1 if (n == 2)
102
+ return fib(n - 1) + fib(n - 2)
103
+ end
104
+
105
+ def self.say_hello
106
+ "hello"
107
+ end
108
+
109
+ end
110
+ ```
111
+
112
+ You can also try this out with build in libraries like MD5 for example
113
+
114
+ ```ruby
115
+ #example.rb
116
+
117
+ Pretentious.spec_for(Digest::MD5) do
118
+ sample = "This is the digest"
119
+ Digest::MD5.hexdigest(sample)
120
+ end
121
+ ```
122
+
123
+ You should get something like:
124
+
125
+ ```ruby
126
+ require 'spec_helper'
127
+
128
+ RSpec.describe Digest::MD5 do
129
+
130
+ it 'should pass current expectations' do
131
+
132
+ sample = "This is the digest"
133
+
134
+ # Digest::MD5::hexdigest when passed "This is the digest" should return 9f12248dcddeda976611d192efaaf72a
135
+ expect( Digest::MD5.hexdigest(sample) ).to eq("9f12248dcddeda976611d192efaaf72a")
136
+
137
+ end
138
+ end
139
+ ```
140
+
141
+ Only RSpec is support at this point. But other testing frameworks should be trivial to add support to.
142
+
143
+ ## Limitations
144
+
145
+ Computers are bad at mind reading (for now) and they don't really know your expectation of "correctness", as such
146
+ it assumes your code is correct and can only use equality based matchers. It can also only reliably match
147
+ primitive data types and hashs and arrays to degree. More complex expectations are unfortunately left for the human to resolve.
148
+
149
+ Also do note that it tries its best to determine how your fixtures are created, as well as the types
150
+ of your parameters and does so by figuring out the components that your object needs. Failure can happen during this process.
151
+
152
+ Finally, Limit this gem for test environments only.
153
+
154
+ ## Bugs
155
+
156
+ This is the first iteration and a lot of broken things could happen
157
+
158
+ ## Contributing
159
+
160
+ 1. Fork it (https://github.com/jedld/pretentious.git)
161
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
162
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
163
+ 4. Push to the branch (`git push origin my-new-feature`)
164
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/ddtgen ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pretentious'
4
+ require 'optparse'
5
+ require 'ripper'
6
+ require "readline"
7
+ require 'json'
8
+ require 'fileutils'
9
+
10
+ $test_framework = :rspec
11
+ $output_folder = 'spec'
12
+
13
+ # ddtgen example.rb -t rspec -o rspec/
14
+ options = OptionParser.new do |o|
15
+ o.banner =
16
+ "Usage: ddtgen FILENAME [options] # Generates tests using the specified example file\n"
17
+ o.separator ""
18
+ o.separator "options:"
19
+ o.on('-t','--test-type','test framework to use (default :rspec)') { |b| $test_framework = b.to_sym}
20
+ o.on('-o','--output-dir','folder to place the files in') { |b| $output_folder = b}
21
+ o.parse!
22
+ end
23
+
24
+
25
+ filename = ARGV[0]
26
+
27
+ if filename.nil?
28
+ puts "an example file is required."
29
+ puts options
30
+ exit(1)
31
+ end
32
+
33
+ example_body = ""
34
+
35
+ index = 0
36
+ File.open(filename, "r") do |f|
37
+ f.each_line do |line|
38
+ example_body << "#{line}\n"
39
+ end
40
+ end
41
+
42
+ eval(example_body)
43
+
44
+ #collect results
45
+
46
+ FileUtils.mkdir_p $output_folder
47
+
48
+ module DdtUtils
49
+ def self.to_underscore(str)
50
+ str.gsub(/(.)([A-Z])/,'\1_\2').downcase
51
+ end
52
+ end
53
+
54
+ Pretentious.last_results.each { |klass, result|
55
+
56
+ klass_name_parts = klass.name.split('::')
57
+ last_part = klass_name_parts.pop
58
+
59
+ filename = File.join($output_folder,"#{DdtUtils.to_underscore(last_part)}_spec.rb")
60
+ File.open(filename, 'w') {
61
+ |f| f.write(result)
62
+ }
63
+ puts "#{filename}"
64
+ }
65
+
66
+ filename = File.join($output_folder,"spec_helper.rb")
67
+ unless File.exists?(filename)
68
+ File.open(filename, 'w') {
69
+ |f| f.write("#Place your requires here")
70
+ }
71
+ puts "#{filename}"
72
+ end
data/example.rb ADDED
@@ -0,0 +1,93 @@
1
+ require 'digest/md5'
2
+
3
+ class Fibonacci
4
+
5
+ def fib(n)
6
+ return 0 if (n == 0)
7
+ return 1 if (n == 1)
8
+ return 1 if (n == 2)
9
+ return fib(n - 1) + fib(n - 2)
10
+ end
11
+
12
+ def self.say_hello
13
+ "hello"
14
+ end
15
+
16
+ end
17
+
18
+ class TestClass1
19
+
20
+ def initialize(message)
21
+ @message = message
22
+ end
23
+
24
+ def print_message
25
+ puts @message
26
+ end
27
+
28
+ def something_is_wrong
29
+ raise StandardError.new
30
+ end
31
+ end
32
+
33
+ class TestClass2
34
+ def initialize(message)
35
+ @message = {message: message}
36
+ end
37
+
38
+ def print_message
39
+ puts @message[:message]
40
+ end
41
+ end
42
+
43
+ class TestClass3
44
+
45
+ def initialize(testclass1, testclass2)
46
+ @class1 = testclass1
47
+ @class2 = testclass2
48
+ end
49
+
50
+ def get_hash
51
+ {hello: "world", message: {another: :hash}}
52
+ end
53
+
54
+ def show_messages
55
+ @class1.print_message
56
+ @class2.print_message
57
+ "awesome!!!"
58
+ end
59
+
60
+ end
61
+
62
+ Pretentious.spec_for(Fibonacci) do
63
+
64
+
65
+ instance = Fibonacci.new
66
+
67
+ (1..10).each do |n|
68
+ instance.fib(n)
69
+ end
70
+
71
+ Fibonacci.say_hello
72
+
73
+ end
74
+
75
+ Pretentious.spec_for(TestClass1, TestClass2, TestClass3) do
76
+
77
+ another_object = TestClass1.new("test")
78
+ test_class_one = TestClass1.new({hello: "world", test: another_object, arr_1: [1,2,3,4,5, another_object],
79
+ sub_hash: {yes: true, obj: another_object}})
80
+ test_class_two = TestClass2.new("This is message 2")
81
+
82
+ class_to_test = TestClass3.new(test_class_one, test_class_two)
83
+ class_to_test.show_messages
84
+
85
+ class_to_test = TestClass3.new(test_class_one, test_class_two)
86
+ class_to_test.show_messages
87
+
88
+ end
89
+
90
+ Pretentious.spec_for(Digest::MD5) do
91
+ sample = "This is the digest"
92
+ Digest::MD5.hexdigest(sample)
93
+ end
@@ -0,0 +1,370 @@
1
+ require "pretentious/version"
2
+ require "pretentious/rspec_generator"
3
+ require 'binding_of_caller'
4
+ require 'pretentious/deconstructor'
5
+
6
+ module Pretentious
7
+
8
+ def self.spec_for(*klasses, &block)
9
+ @results = @results || {}
10
+ @results.merge!(Pretentious::Generator.generate_for(*klasses, &block))
11
+ end
12
+
13
+ def self.clear_results
14
+ @results = {}
15
+ end
16
+
17
+ def self.last_results
18
+ @results
19
+ end
20
+
21
+ def self.value_ize(value, let_variables, declared_names)
22
+ if (value.kind_of? String)
23
+ "#{value.dump}"
24
+ elsif (value.is_a? Symbol)
25
+ ":#{value.to_s}"
26
+ elsif (value.is_a? Hash)
27
+ Pretentious::Deconstructor.pick_name(let_variables, value.object_id, declared_names)
28
+ else
29
+ "#{value.to_s}"
30
+ end
31
+ end
32
+
33
+ def self.watch(&block)
34
+ Pretentious::Generator.watch_new_instances
35
+ block.call
36
+ Pretentious::Generator.unwatch_new_instances
37
+ end
38
+
39
+ class Generator
40
+
41
+ def self.impostor_for(module_space, klass)
42
+ newStandInKlass = Class.new()
43
+ name = klass.name
44
+ module_space.const_set "#{name.split('::').last}Impostor", newStandInKlass
45
+
46
+ common_snippet = "
47
+ @_method_calls = @_method_calls || []
48
+ @_method_calls_by_method = @_method_calls_by_method || {}
49
+ @_methods_for_test = @_methods_for_test || []
50
+ @_let_variables = @_let_variables || {}
51
+
52
+ caller_context = binding.of_caller(1)
53
+ #puts \"local_variables\"
54
+ v_locals = caller_context.eval('local_variables')
55
+
56
+ v_locals.each { |v|
57
+ variable_value = caller_context.eval(\"\#{v.to_s}\")
58
+ @_let_variables[variable_value.object_id] = v
59
+ }
60
+
61
+ info_block = {}
62
+ info_block[:method] = method_sym
63
+ info_block[:params] = arguments
64
+ info_block[:names] = @_instance.method(method_sym).parameters
65
+
66
+ begin
67
+ if (@_instance.methods.include? method_sym)
68
+ result = @_instance.send(method_sym, *arguments, &block)
69
+ else
70
+ result = @_instance.send(:method_missing, method_sym, *arguments, &block)
71
+ end
72
+ info_block[:result] = result
73
+ rescue Exception=>e
74
+ info_block[:result] = e
75
+ rescue StandardError=>e
76
+ info_block[:result] = e
77
+ end
78
+
79
+ @_method_calls << info_block
80
+
81
+ if (@_method_calls_by_method[method_sym].nil?)
82
+ @_method_calls_by_method[method_sym] = []
83
+ end
84
+
85
+ @_method_calls_by_method[method_sym] << info_block
86
+ raise e if (e.kind_of? Exception)
87
+ result"
88
+
89
+ newStandInKlass.class_eval("
90
+ def setup_instance(*args, &block)
91
+ @_instance = #{klass.name}_ddt.new(*args, &block)
92
+ end
93
+ ")
94
+
95
+ newStandInKlass.class_exec do
96
+ def initialize(*args, &block)
97
+
98
+ @_instance_init = {params: [], block: nil}
99
+
100
+ @_instance_init[:params] = args
101
+ @_instance_init[:block] = block
102
+
103
+ setup_instance(*args, &block)
104
+
105
+
106
+ @_method_calls = []
107
+ @_method_calls_by_method = {}
108
+ @_methods_for_test = []
109
+ @_let_variables = {}
110
+
111
+
112
+ @_init_let_variables = {}
113
+
114
+ caller_context = binding.of_caller(2)
115
+ v_locals = caller_context.eval('local_variables')
116
+
117
+ v_locals.each { |v|
118
+ variable_value = caller_context.eval("#{v.to_s}")
119
+ @_init_let_variables[variable_value.object_id] = v
120
+ }
121
+
122
+ self.class._add_instances(self)
123
+ end
124
+
125
+ def _init_arguments
126
+ @_instance_init
127
+ end
128
+
129
+ def test_class
130
+ @_instance.class
131
+ end
132
+
133
+ def include_for_tests(method_list = [])
134
+ @_methods_for_test = @_methods_for_test + method_list
135
+ end
136
+
137
+ def let_variables
138
+ @_let_variables
139
+ end
140
+
141
+ def init_let_variables
142
+ @_init_let_variables
143
+ end
144
+
145
+ def method_calls_by_method
146
+ @_method_calls_by_method
147
+ end
148
+
149
+ def method_calls
150
+ @_method_calls
151
+ end
152
+
153
+ def to_s
154
+ @_instance.to_s
155
+ end
156
+
157
+ def ==(other)
158
+ @_instance==other
159
+ end
160
+
161
+ def kind_of?(klass)
162
+ @_instance.kind_of? klass
163
+ end
164
+
165
+ def methods
166
+ @_instance.methods + [:method_calls]
167
+ end
168
+
169
+ def freeze
170
+ @_instance.freeze
171
+ end
172
+
173
+ def hash
174
+ @instance.hash
175
+ end
176
+
177
+ def inspect
178
+ @_instance.inspect
179
+ end
180
+
181
+ def is_a?(something)
182
+ @_instance.is_a? something
183
+ end
184
+
185
+ class << self
186
+ def _add_instances(instance)
187
+ @_instances = @_instances || []
188
+ @_instances << instance unless @_instances.include? instance
189
+ end
190
+
191
+ def let_variables
192
+ @_let_variables
193
+ end
194
+
195
+ def method_calls_by_method
196
+ @_method_calls_by_method
197
+ end
198
+
199
+ def method_calls
200
+ @_method_calls
201
+ end
202
+
203
+ def _add_instances(instance)
204
+ @_instances = @_instances || []
205
+ @_instances << instance unless @_instances.include? instance
206
+ end
207
+
208
+ def _instances
209
+ @_instances
210
+ end
211
+
212
+ end
213
+
214
+ end
215
+
216
+ newStandInKlass.class_eval("
217
+ def method_missing(method_sym, *arguments, &block)
218
+ #puts \"\#{method_sym} \#{arguments}\"
219
+ #{common_snippet}
220
+ end
221
+
222
+ class << self
223
+
224
+ def test_class
225
+ #{klass.name}
226
+ end
227
+
228
+ def method_missing(method_sym, *arguments, &block)
229
+ #puts \"method \#{method_sym.to_s}\"
230
+ _add_instances(self)
231
+ @_instance = #{klass.name}_ddt
232
+ #{common_snippet}
233
+ end
234
+ end
235
+ ")
236
+
237
+ newStandInKlass
238
+ end
239
+
240
+ def self.generate_for(*klasses_or_instances, &block)
241
+ all_results = {}
242
+ klasses = []
243
+
244
+ klasses_or_instances.each { |klass_or_instance|
245
+ klass = klass_or_instance.class == Class ? klass_or_instance : klass_or_instance.class
246
+
247
+
248
+ klass_name_parts = klass.name.split('::')
249
+ last_part = klass_name_parts.pop
250
+
251
+ module_space = Object
252
+
253
+ if (klass_name_parts.size > 0)
254
+ klass_name_parts.each do |part|
255
+ module_space = module_space.const_get(part)
256
+ end
257
+ end
258
+
259
+ newStandInKlass = impostor_for module_space, klass
260
+
261
+ module_space.send(:remove_const,last_part.to_sym)
262
+ module_space.const_set("#{last_part}_ddt", klass)
263
+ module_space.const_set("#{last_part}", newStandInKlass)
264
+
265
+ klasses << [module_space, klass, last_part, newStandInKlass]
266
+ }
267
+
268
+ watch_new_instances
269
+
270
+ block.call
271
+
272
+ unwatch_new_instances
273
+
274
+ klasses.each { |module_space, klass, last_part, newStandInKlass|
275
+
276
+ module_space.send(:remove_const,"#{last_part}Impostor".to_sym)
277
+ module_space.send(:remove_const,"#{last_part}".to_sym)
278
+ module_space.const_set(last_part, klass)
279
+ module_space.send(:remove_const,"#{last_part}_ddt".to_sym)
280
+
281
+ test_generator = Pretentious::RspecGenerator.new
282
+ test_generator.begin_spec(klass)
283
+ num = 1
284
+
285
+ newStandInKlass._instances.each do |instance|
286
+ test_generator.generate(instance, num)
287
+ num+=1
288
+ end
289
+
290
+ test_generator.end_spec
291
+
292
+ result = all_results[klass]
293
+ if result.nil?
294
+ all_results[klass] = []
295
+ end
296
+
297
+ all_results[klass] = test_generator.output
298
+
299
+ }
300
+
301
+ all_results
302
+ end
303
+
304
+ def self.watch_new_instances
305
+ Object.class_eval do
306
+ def _get_init_arguments
307
+ @_init_arguments
308
+ end
309
+
310
+ def _set_init_arguments(*args, &block)
311
+ @_init_arguments = @_init_arguments || {}
312
+ @_init_arguments[:params] = args
313
+ @_init_arguments[:block] = block
314
+ @_variable_names= {}
315
+
316
+ index = 0
317
+ params = method(:initialize).parameters
318
+
319
+ args.each do |arg|
320
+ p = params[index]
321
+ if p.size > 1
322
+ @_variable_names[arg.object_id] = p[1].to_s
323
+ end
324
+ index+=1
325
+ end
326
+
327
+ end
328
+
329
+ def _variable_map
330
+ @_variable_names
331
+ end
332
+
333
+ def _deconstruct
334
+ Pretentious::Deconstructor.new().deconstruct(self)
335
+ end
336
+
337
+ def _deconstruct_to_ruby(indentation = 0)
338
+ Pretentious::Deconstructor.new().deconstruct_to_ruby(indentation, _variable_map, self)
339
+ end
340
+
341
+ end
342
+
343
+ Class.class_eval do
344
+ alias_method :_ddt_old_new, :new
345
+
346
+ def new(*args, &block)
347
+ instance = _ddt_old_new(*args, &block)
348
+ instance._set_init_arguments(*args, &block)
349
+ instance
350
+ end
351
+ end
352
+ end
353
+
354
+ def self.clean_watches
355
+ Class.class_eval do
356
+ remove_method :new
357
+ alias_method :new, :_ddt_old_new
358
+ end
359
+ end
360
+
361
+ def self.unwatch_new_instances
362
+ Class.class_eval do
363
+ remove_method :new
364
+ alias_method :new, :_ddt_old_new
365
+ end
366
+ end
367
+
368
+ end
369
+
370
+ end