self_identity 0.1.0

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: 76cc850d44cf9d6588f6cd346620dee7941f6d28
4
+ data.tar.gz: cff98d7975aba2c313bd8f4f41abdf8a91ae3be0
5
+ SHA512:
6
+ metadata.gz: a8e3b1582a4093f61a57eff22955a1d9e2df58b0dd191156cfd952f216fa2b2532c0b6cfea99ce09fe508ba672a32237bc1f1d5d431a51787c73f259b44354ff
7
+ data.tar.gz: b93ae7bda9e8c6e2205cec178e642bffb51174aff16046bfb50486133ae6a834c362c76702e9c71f35204fb90f734760a10850d0e6096f5e81f5d51f8b2a74d0
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .self_identity
2
+ Gemfile.lock
3
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Chris Olstrom and Justin Scott
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ self_identity
2
+ =============
3
+
4
+ Helping code understand itself, an adventure in alternative testing methodology.
5
+
6
+ Usage
7
+ -----
8
+
9
+ `require 'self_identity'` and your code will record its method dependencies each time it runs.
10
+
11
+ From the working directory the script was run from, you'll find a [Moneta](https://github.com/minad/moneta) file storage with the following arrays and element formats:
12
+
13
+ ### calls
14
+
15
+ - __name__: the method called
16
+ - __input_reference__: object_id's of the input
17
+ - __input__: the given input array
18
+
19
+ ### returns
20
+
21
+ - __name__: the method returned from
22
+ - __output_reference__: object_id of the output
23
+ - __output__: the generated output
24
+
25
+ ### dependencies
26
+
27
+ - __output__: the object passed between the methods
28
+ - __from__: the method returned from
29
+ - __to__: the method called
30
+
31
+ Background
32
+ ----------
33
+
34
+ At the end of 2014, @brixen posted a fascinating rant on the state of RubySpec. One bit stuck out as inspiring: "Ruby is what Ruby does."
35
+
36
+ MRI 1.8 had pretty spotty (or no) test coverage. When confirming the accuracy of Rubinius in the face of an absent or incomplete test suite, @brixen asserted that "The way a Ruby program behaves is the definition of Ruby". From this assertion came RubySpec, a comprehensive suite of specifications describing "what Ruby does" so alternative implementations could do the same thing.
37
+
38
+ self_identity applies this mindset to automated testing.
39
+
40
+ Objectives
41
+ ----------
42
+
43
+ 1. Be able to define method signatures. Given input x (of type a) to method m, the output will be y (of type b).
44
+ 2. Be able to define what qualifies as valid input. For input x, what is it required to ```respond_to?``` within method m.
45
+ 3. Be able to define what qualifies as valid output. For output y from method m, what will it be expected to ```respond_to?``` from creation until it is reaped by the garbage collection valkyries.
46
+
47
+ _Together, these allow a program to assert its identity: the collection of canonical method signatures and object interactions that state "This is what I am, and how I operate."_
48
+
49
+ Notes
50
+ -----
51
+
52
+ This is not a replacement for *good* tests, or specs. It does not validate that the code is correct (beyond the syntax checking inherent in running the program), or that it performs as expected. However, being able to assert that "it did what it did last time, given the same conditions" is better than untested code.
53
+
54
+ Authors
55
+ -------
56
+
57
+ Chris Olstrom and Justin Scott
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rake'
2
+
3
+ task :test do
4
+ require_relative 'kintama/self_identity'
5
+ end
@@ -0,0 +1,45 @@
1
+ ---
2
+ - name: basic_usage
3
+ dependencies:
4
+ - :output: [1]
5
+ :from: :foo
6
+ :to: :bar
7
+
8
+ - name: nested_calls
9
+ dependencies:
10
+ - :output: [1]
11
+ :from: :foo
12
+ :to: :bar
13
+ - :output: [1]
14
+ :from: :foo
15
+ :to: :baz
16
+
17
+ - name: calls_and_returns
18
+ calls:
19
+ - :name: :foo
20
+ :input: [[]]
21
+ - :name: :bar
22
+ :input: [[1]]
23
+ returns:
24
+ - :name: :foo
25
+ :output: [1]
26
+ - :name: :bar
27
+ :output: [1, 2]
28
+
29
+ - name: passthrough_methods
30
+ dependencies:
31
+ - :output: [1]
32
+ :from: :foo
33
+ :to: :bar
34
+ - :output: [1]
35
+ :from: :foo
36
+ :to: :baz
37
+ - :output: [1]
38
+ :from: :bar
39
+ :to: :baz
40
+
41
+ - name: tracking_id_not_value
42
+ dependencies:
43
+ - :output: [1,2]
44
+ :from: :bar
45
+ :to: :baz
@@ -0,0 +1,12 @@
1
+ require_relative '../../lib/self_identity'
2
+
3
+ def foo(array)
4
+ array.push 1
5
+ end
6
+
7
+ def bar(array)
8
+ array.push 2
9
+ end
10
+
11
+ var = foo []
12
+ var = bar var
@@ -0,0 +1,12 @@
1
+ require_relative '../../lib/self_identity'
2
+
3
+ def foo(array)
4
+ array.push 1
5
+ end
6
+
7
+ def bar(array)
8
+ array.push 2
9
+ end
10
+
11
+ var = foo []
12
+ var = bar var
@@ -0,0 +1,17 @@
1
+ require_relative '../../lib/self_identity'
2
+
3
+ def foo(array)
4
+ array.push 1
5
+ end
6
+
7
+ def bar(array)
8
+ baz array
9
+ array.push 2
10
+ end
11
+
12
+ def baz(array)
13
+ # do work with array
14
+ end
15
+
16
+ var = foo []
17
+ var = bar var
@@ -0,0 +1,18 @@
1
+ require_relative '../../lib/self_identity'
2
+
3
+ def foo(array)
4
+ array.push 1
5
+ end
6
+
7
+ def bar(array)
8
+ # do work with array
9
+ array
10
+ end
11
+
12
+ def baz(array)
13
+ array.push 2
14
+ end
15
+
16
+ var = foo []
17
+ var = bar var
18
+ var = baz var
@@ -0,0 +1,17 @@
1
+ require_relative '../../lib/self_identity'
2
+
3
+ def foo(array)
4
+ array.push 1
5
+ end
6
+
7
+ def bar(array)
8
+ array.push 2
9
+ end
10
+
11
+ def baz(array)
12
+ array.push 3
13
+ end
14
+
15
+ var = foo []
16
+ var = bar [1]
17
+ var = baz var
@@ -0,0 +1,38 @@
1
+ require 'kintama'
2
+ require 'yaml'
3
+ require 'moneta'
4
+ #require_relative 'helpers'
5
+ # TODO: clean this up with helpers and such
6
+
7
+ $scripts = YAML.load_file "#{__dir__}/expected/outcomes.yaml"
8
+
9
+ def remove_reference_keys_for(hash)
10
+ hash.reject! { |key| key.match /reference/ }
11
+ end
12
+
13
+ given 'self_identity is required' do
14
+ setup do
15
+ @script_dir = "#{__dir__}/scripts"
16
+ @storage = Moneta.new :File, dir: '.self_identity'
17
+ end
18
+
19
+ $scripts.each do |script|
20
+ context "running scripts for #{script['name']}" do
21
+ setup do
22
+ script_path = "#{@script_dir}/#{script['name']}.rb"
23
+ thread = Thread.new { system "ruby #{script_path}" }
24
+ thread.join
25
+ end
26
+
27
+ ['calls', 'returns', 'dependencies'].each do |data|
28
+ if script[data]
29
+ should "archive method #{data}" do
30
+ actual = @storage.fetch data, []
31
+ actual.each { |hash| remove_reference_keys_for hash }
32
+ assert_equal script[data], actual
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,48 @@
1
+ def safe_clone(object)
2
+ begin
3
+ object.clone
4
+ rescue TypeError => e
5
+ object
6
+ end
7
+ end
8
+
9
+ def sanitize(object)
10
+ if object.is_a? Proc
11
+ object.to_s
12
+ else
13
+ safe_clone object
14
+ end
15
+ end
16
+
17
+ def new_method_call(from:)
18
+ parameters = from.binding.eval "method(__method__).parameters.map { |p| eval p.last.to_s }"
19
+ {
20
+ name: from.method_id,
21
+ input_reference: parameters.map(&:object_id),
22
+ input: parameters.map { |parameter| sanitize parameter }
23
+ }
24
+ end
25
+
26
+ def new_method_return(from:)
27
+ {
28
+ name: from.method_id,
29
+ output_reference: from.return_value.object_id,
30
+ output: sanitize(from.return_value)
31
+ }
32
+ end
33
+
34
+ def returns_that_pass_to(current)
35
+ @returns.select do |previous|
36
+ current[:input_reference].detect { |given_input| given_input.equal? previous[:output_reference] }
37
+ end
38
+ end
39
+
40
+ def dependencies_for(method_call)
41
+ returns_that_pass_to(method_call).map do |method_return|
42
+ {
43
+ output: method_return[:output],
44
+ from: method_return[:name],
45
+ to: method_call[:name]
46
+ }
47
+ end
48
+ end
@@ -0,0 +1,52 @@
1
+ require 'moneta'
2
+ require_relative 'data_constructors'
3
+
4
+ storage = Moneta.new :File, dir: '.self_identity'
5
+ storage['calls'] = []
6
+ storage['returns'] = []
7
+ storage['dependencies'] = []
8
+
9
+ @trace = TracePoint.trace do |trace|
10
+ @calls ||= []
11
+ @returns ||= []
12
+ @dependencies ||= []
13
+ case trace.event
14
+ when :call
15
+ method_call = new_method_call from: trace
16
+ @dependencies.concat dependencies_for(method_call)
17
+ @calls.push method_call
18
+ when :return
19
+ @returns.push new_method_return(from: trace)
20
+ when :b_return
21
+ # not sure how to hook into method blocks
22
+ # __method__ returns the method it was called from
23
+ when :c_return
24
+ # might need a C extension to hook into C calls
25
+ # __method__ returns 'main'
26
+ else
27
+ end
28
+ script_name = File.basename($PROGRAM_NAME, '.rb')
29
+ storage.store 'calls', @calls
30
+ storage.store 'returns', @returns
31
+ storage.store 'dependencies', @dependencies
32
+ end
33
+
34
+ module SelfIdentity
35
+ @@trace = @trace
36
+
37
+ def self.enabled?
38
+ @@trace.enabled?
39
+ end
40
+
41
+ def self.enable
42
+ @@trace.enable
43
+ end
44
+
45
+ def self.disable
46
+ @@trace.disable
47
+ end
48
+ end
49
+
50
+ @trace = nil
51
+
52
+ # don't put anything here unless you want it traced
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = 'self_identity'
3
+ gem.version = '0.1.0'
4
+ gem.licenses = 'MIT'
5
+ gem.authors = ['Justin Scott','Chris Olstrom']
6
+ gem.email = 'jvscott@gmail.com'
7
+ gem.homepage = 'http://www.github.com/colstrom/self_identity/'
8
+ gem.summary = 'Self Identity'
9
+ gem.description = 'Allowing code to understand its own functionality.'
10
+
11
+ gem.files = `git ls-files`.split("\n")
12
+ gem.test_files = `git ls-files -- kintama/**/*`.split("\n")
13
+
14
+ gem.add_runtime_dependency 'moneta'
15
+
16
+ gem.add_development_dependency 'rake'
17
+ gem.add_development_dependency 'kintama'
18
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: self_identity
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Justin Scott
8
+ - Chris Olstrom
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-03-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: moneta
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: kintama
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ description: Allowing code to understand its own functionality.
57
+ email: jvscott@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE
65
+ - README.md
66
+ - Rakefile
67
+ - kintama/expected/outcomes.yaml
68
+ - kintama/scripts/basic_usage.rb
69
+ - kintama/scripts/calls_and_returns.rb
70
+ - kintama/scripts/nested_calls.rb
71
+ - kintama/scripts/passthrough_methods.rb
72
+ - kintama/scripts/tracking_id_not_value.rb
73
+ - kintama/self_identity.rb
74
+ - lib/data_constructors.rb
75
+ - lib/self_identity.rb
76
+ - self_identity.gemspec
77
+ homepage: http://www.github.com/colstrom/self_identity/
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.4.5
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Self Identity
101
+ test_files:
102
+ - kintama/expected/outcomes.yaml
103
+ - kintama/scripts/basic_usage.rb
104
+ - kintama/scripts/calls_and_returns.rb
105
+ - kintama/scripts/nested_calls.rb
106
+ - kintama/scripts/passthrough_methods.rb
107
+ - kintama/scripts/tracking_id_not_value.rb