cognitive_distance 0.0.1.pre

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.
Files changed (40) hide show
  1. data/.autotest +19 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE.md +221 -0
  6. data/README.md +50 -0
  7. data/Rakefile +14 -0
  8. data/cognitive_distance.gemspec +23 -0
  9. data/lib/cognitive_distance.rb +23 -0
  10. data/lib/cognitive_distance/measurements.rb +16 -0
  11. data/lib/cognitive_distance/measurements/distinct_module_hops.rb +19 -0
  12. data/lib/cognitive_distance/measurements/measurement.rb +7 -0
  13. data/lib/cognitive_distance/measurements/module_hops.rb +17 -0
  14. data/lib/cognitive_distance/presenters.rb +2 -0
  15. data/lib/cognitive_distance/presenters/graph_to_dot.rb +11 -0
  16. data/lib/cognitive_distance/structures.rb +9 -0
  17. data/lib/cognitive_distance/structures/call_node.rb +74 -0
  18. data/lib/cognitive_distance/structures/call_node_root.rb +29 -0
  19. data/lib/cognitive_distance/structures/call_tree.rb +34 -0
  20. data/lib/cognitive_distance/structures/graph.rb +77 -0
  21. data/lib/cognitive_distance/structures/missing_call_context.rb +10 -0
  22. data/lib/cognitive_distance/tracer.rb +32 -0
  23. data/lib/cognitive_distance/transforms.rb +5 -0
  24. data/lib/cognitive_distance/transforms/call_tree_to_module_boundary_graph.rb +30 -0
  25. data/lib/cognitive_distance/version.rb +3 -0
  26. data/spec/cognitive_distance/measurements/distinct_module_hops_spec.rb +52 -0
  27. data/spec/cognitive_distance/measurements/measurement_spec.rb +44 -0
  28. data/spec/cognitive_distance/measurements/module_hops_spec.rb +54 -0
  29. data/spec/cognitive_distance/measurements_spec.rb +19 -0
  30. data/spec/cognitive_distance/presenters/graph_to_dot_spec.rb +26 -0
  31. data/spec/cognitive_distance/structures/call_node_root_spec.rb +31 -0
  32. data/spec/cognitive_distance/structures/call_node_spec.rb +91 -0
  33. data/spec/cognitive_distance/structures/call_tree_spec.rb +55 -0
  34. data/spec/cognitive_distance/structures/graph_spec.rb +142 -0
  35. data/spec/cognitive_distance/structures/missing_call_context_spec.rb +21 -0
  36. data/spec/cognitive_distance/tracer_spec.rb +57 -0
  37. data/spec/cognitive_distance/transforms/call_tree_to_module_boundary_graph_spec.rb +57 -0
  38. data/spec/spec_dummies.rb +60 -0
  39. data/spec/spec_helper.rb +12 -0
  40. metadata +111 -0
@@ -0,0 +1,19 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe CognitiveDistance::Measurements do
4
+ class SuckySuckSucks
5
+ class << self
6
+ attr_reader :lameness_args
7
+ def lameness *args
8
+ @lameness_args = args
9
+ end
10
+ end
11
+ end
12
+
13
+ it "registers a measurement" do
14
+ CognitiveDistance::Measurements.register_measurement SuckySuckSucks, :lameness, :beefcake
15
+ CognitiveDistance::Measurements.measure_beefcake :x, :y, :z
16
+ SuckySuckSucks.lameness_args.must_equal [:x, :y, :z]
17
+ end
18
+ end
19
+
@@ -0,0 +1,26 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe CognitiveDistance::Presenters::GraphToDot do
4
+ before do
5
+ @presenter = CognitiveDistance::Presenters::GraphToDot.new
6
+ @graph = CognitiveDistance::Structures::Graph.new
7
+ @graph.link :a, :b, :c
8
+ @graph.link :b, :c
9
+ end
10
+
11
+ it "converts a graph to dot format" do
12
+ dotted = @presenter.present @graph
13
+ dotted.must_equal "digraph \"graphname\" {\n\"a\" -> \"b\";\n\"a\" -> \"c\";\n\"b\" -> \"c\";\n}"
14
+ end
15
+
16
+ it "names the graph" do
17
+ dotted = @presenter.present @graph, "MyGraph"
18
+ dotted.must_equal "digraph \"MyGraph\" {\n\"a\" -> \"b\";\n\"a\" -> \"c\";\n\"b\" -> \"c\";\n}"
19
+ end
20
+
21
+ it "uses a block to map nodes to names" do
22
+ dotted = @presenter.present(@graph, "Blocked!") { |v| "Node #{v}" }
23
+ dotted.must_equal "digraph \"Blocked!\" {\n\"Node a\" -> \"Node b\";\n\"Node a\" -> \"Node c\";\n\"Node b\" -> \"Node c\";\n}"
24
+ end
25
+ end
26
+
@@ -0,0 +1,31 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe CognitiveDistance::Structures::CallNodeRoot do
4
+ before do
5
+ @node = CognitiveDistance::Structures::CallNodeRoot.new
6
+ end
7
+
8
+ it "should always return itself on pop!" do
9
+ @node.pop!.must_equal @node
10
+ end
11
+
12
+ it "should not have a context" do
13
+ @node.context.is_a?(CognitiveDistance::Structures::MissingCallContext).must_equal true
14
+ end
15
+
16
+ it "should not yield itself when enumerating" do
17
+ @node.inject(false) { |found, n|
18
+ found || n.equal?(@node)
19
+ }.must_equal false
20
+ end
21
+
22
+ it "should not include itself when converting to an array" do
23
+ c1 = @node.push! 1, nil, nil, nil, nil
24
+ c2 = @node.push! 2, nil, nil, nil, nil
25
+ @node.to_a.must_equal [
26
+ [c1, [] ],
27
+ [c2, [] ]
28
+ ]
29
+ end
30
+ end
31
+
@@ -0,0 +1,91 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe CognitiveDistance::Structures::CallNode do
4
+ before do
5
+ @parent = Object.new
6
+ @node = CognitiveDistance::Structures::CallNode.new(@parent)
7
+ @node.trace_class = 0
8
+ end
9
+
10
+ it "sets a parent" do
11
+ @node.parent.must_equal @parent
12
+ end
13
+
14
+ it "returns its parent on pop!" do
15
+ @node.pop!.must_equal @parent
16
+ end
17
+
18
+ it "returns a newly created child on push!" do
19
+ child = @node.push! 1, 2, 3, 4, 5
20
+ @node.children.last.must_equal child
21
+ end
22
+
23
+ it "completely sets up the new child" do
24
+ child = @node.push! 1, 2, 3, 4, 5
25
+ child.trace_class.must_equal 1
26
+ child.trace_method.must_equal 2
27
+ child.trace_file.must_equal 3
28
+ child.trace_line.must_equal 4
29
+ child.trace_binding.must_equal 5
30
+ child.parent.must_equal @node
31
+ child.children.must_be_empty
32
+ end
33
+
34
+ it "generates a context from a binding" do
35
+ method = [].method(:empty?)
36
+ binding = method.to_proc.binding
37
+ @node.trace_binding = binding
38
+ @node.context.must_equal method
39
+ end
40
+
41
+ it "yields itself and then each child when enumerating" do
42
+ # Make a child
43
+ @node.trace_class = 0
44
+ child1 = @node.push!(1, nil, nil, nil, nil)
45
+ # And two grandchildren
46
+ child1.push!(11, nil, nil, nil, nil)
47
+ child1.push!(12, nil, nil, nil, nil)
48
+ # Make another child
49
+ @node.push!(2, nil, nil, nil, nil)
50
+ @node.map(&:trace_class).must_equal [0, 1, 11, 12, 2]
51
+ end
52
+
53
+ it "is empty when it has no children" do
54
+ @node.empty?.must_equal true
55
+ @node.push! 1, nil, nil, nil, nil
56
+ @node.empty?.must_equal false
57
+ end
58
+
59
+ it "determines size by the number of children plus their sizes" do
60
+ child1 = @node.push! nil, nil, nil, nil, nil
61
+ child2 = @node.push! nil, nil, nil, nil, nil
62
+ child1.push! nil, nil, nil, nil, nil
63
+ child1.push! nil, nil, nil, nil, nil
64
+ child2.push! nil, nil, nil, nil, nil
65
+ # node => 2 children, child1 => 2 children, child2 => 1 child
66
+ @node.size.must_equal 5
67
+ end
68
+
69
+ it "freezes its children when freezing" do
70
+ child1 = @node.push! nil, nil, nil, nil, nil
71
+ child1.push! nil, nil, nil, nil, nil
72
+ @node.freeze
73
+ @node.all?(&:frozen?).must_equal true
74
+ @node.children.frozen?.must_equal true
75
+ end
76
+
77
+ it "converts to an array" do
78
+ c1 = @node.push! 1, nil, nil, nil, nil
79
+ c2 = @node.push! 2, nil, nil, nil, nil
80
+ c1_1 = c1.push! 3, nil, nil, nil, nil
81
+ c1_1_1 = c1_1.push! 4, nil, nil, nil, nil
82
+ c2_1 = c2.push! 5, nil, nil, nil, nil
83
+ @node.to_a.must_equal [
84
+ @node, [
85
+ [c1, [ [ c1_1, [ [c1_1_1, []] ] ] ] ],
86
+ [c2, [ [ c2_1, [] ] ] ]
87
+ ]
88
+ ]
89
+ end
90
+ end
91
+
@@ -0,0 +1,55 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe CognitiveDistance::Structures::CallTree do
4
+ before do
5
+ @call_tree = CognitiveDistance::Structures::CallTree.new
6
+ end
7
+
8
+ def populate_tree
9
+ @call_tree.called 1, nil, nil, nil, nil
10
+ @call_tree.called 11, nil, nil, nil, nil
11
+ @call_tree.returned 11, nil, nil, nil, nil
12
+ @call_tree.called 12, nil, nil, nil, nil
13
+ @call_tree.returned 12, nil, nil, nil, nil
14
+ @call_tree.returned 1, nil, nil, nil, nil
15
+ @call_tree.called 2, nil, nil, nil, nil
16
+ @call_tree.called 21, nil, nil, nil, nil
17
+ @call_tree.returned 21, nil, nil, nil, nil
18
+ @call_tree.returned 2, nil, nil, nil, nil
19
+ @call_tree.called 3, nil, nil, nil, nil
20
+ @call_tree.returned 3, nil, nil, nil, nil
21
+ end
22
+
23
+ it "returns the root node" do
24
+ @call_tree.root.
25
+ is_a?(CognitiveDistance::Structures::CallNodeRoot).must_equal true
26
+ end
27
+
28
+ it "is empty when there's nothing in the tree" do
29
+ @call_tree.empty?.must_equal true
30
+ @call_tree.called nil, nil, nil, nil, nil
31
+ @call_tree.empty?.must_equal false
32
+ end
33
+
34
+ it "converts to an array by converting the root node" do
35
+ populate_tree
36
+ @call_tree.to_a.must_equal @call_tree.root.to_a
37
+ end
38
+
39
+ it "contains all nodes when enumerated" do
40
+ populate_tree
41
+ @call_tree.map(&:trace_class).must_equal [1, 11, 12, 2, 21, 3]
42
+ end
43
+
44
+ it "calculates size from the children of the root" do
45
+ populate_tree
46
+ @call_tree.size.must_equal 6
47
+ end
48
+
49
+ it "freezes all nodes when freezing the tree" do
50
+ populate_tree
51
+ @call_tree.freeze
52
+ @call_tree.all?(&:frozen?).must_equal true
53
+ end
54
+ end
55
+
@@ -0,0 +1,142 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe CognitiveDistance::Structures::Graph do
4
+ before do
5
+ @graph = CognitiveDistance::Structures::Graph.new
6
+ end
7
+
8
+ class TestSubGraph < CognitiveDistance::Structures::Graph
9
+ end
10
+
11
+ it "is empty when there is nothing in it" do
12
+ @graph.empty?.must_equal true
13
+ @graph.link "vertex 1", "vertex 2"
14
+ @graph.empty?.must_equal false
15
+ end
16
+
17
+ it "records vertices" do
18
+ @graph.vertices.size.must_equal 0
19
+ @graph.link "vertex 1", "vertex 2", "vertex 3", "vertex 4", "vertex 2"
20
+ @graph.link "vertex 2", "vertex 3", "vertex 5", "vertex 1"
21
+ @graph.vertices.size.must_equal 5
22
+ end
23
+
24
+ it "records edges" do
25
+ @graph.edges.size.must_equal 0
26
+ @graph.link "vertex 1", "vertex 2", "vertex 3", "vertex 4", "vertex 2"
27
+ @graph.link "vertex 2", "vertex 3", "vertex 5", "vertex 1"
28
+ @graph.edges.size.must_equal 7
29
+ end
30
+
31
+ it "is enumerable" do
32
+ @graph.class.must_include Enumerable
33
+ end
34
+
35
+ it "enumerates over edges" do
36
+ collected = @graph.map { |e| e }
37
+ collected.must_equal @graph.edges
38
+ end
39
+
40
+ it "returns vertices that share an edge with the given vertex" do
41
+ @graph.link "vertex 1", "vertex 2", "vertex 3"
42
+ @graph.any_edges("vertex 4").size.must_equal 0
43
+ @graph.link "vertex 1", "vertex 4"
44
+ @graph.link "vertex 4", "vertex 2"
45
+ edges = @graph.any_edges("vertex 4")
46
+ edges.size.must_equal 2
47
+ edges.must_include ["vertex 1", "vertex 4"]
48
+ edges.must_include ["vertex 4", "vertex 2"]
49
+ end
50
+
51
+ it "returns vertices that link out of the given vertex" do
52
+ @graph.link "vertex 1", "vertex 2", "vertex 4"
53
+ @graph.out_edges("vertex 4").size.must_equal 0
54
+ @graph.link "vertex 4", "vertex 2"
55
+ edges = @graph.out_edges("vertex 4")
56
+ edges.must_equal [ [ "vertex 4", "vertex 2" ] ]
57
+ end
58
+
59
+ it "returns vertices link into the given vertex" do
60
+ @graph.link "vertex 4", "vertex 2", "vertex 3"
61
+ @graph.in_edges("vertex 4").size.must_equal 0
62
+ @graph.link "vertex 1", "vertex 4"
63
+ edges = @graph.in_edges("vertex 4")
64
+ edges.must_equal [ [ "vertex 1", "vertex 4" ] ]
65
+ end
66
+
67
+ it "records bi-directional links" do
68
+ @graph.bilink "vertex 1", "vertex 2"
69
+ @graph.in_edges("vertex 1").must_equal [ ["vertex 2", "vertex 1"] ]
70
+ @graph.out_edges("vertex 1").must_equal [ ["vertex 1", "vertex 2"] ]
71
+ @graph.in_edges("vertex 2").must_equal [ ["vertex 1", "vertex 2"] ]
72
+ @graph.out_edges("vertex 2").must_equal [ ["vertex 2", "vertex 1"] ]
73
+ end
74
+
75
+ it "converts to an array of edges" do
76
+ @graph.bilink "vertex 1", "vertex 2"
77
+ @graph.link "vertex 1", "vertex 4"
78
+ edge_array = @graph.to_a
79
+ edge_array.size.must_equal 3
80
+ edge_array.must_include ["vertex 1", "vertex 2"]
81
+ edge_array.must_include ["vertex 2", "vertex 1"]
82
+ edge_array.must_include ["vertex 1", "vertex 4"]
83
+ end
84
+
85
+ it "is always equal to itself" do
86
+ (@graph == @graph).must_equal true
87
+ @graph.link "a", "b"
88
+ (@graph == @graph).must_equal true
89
+ end
90
+
91
+ it "is equal (==) to another graph iff they have the same edges" do
92
+ graph2 = CognitiveDistance::Structures::Graph.new
93
+ @graph.bilink "vertex 1", "vertex 2"
94
+ @graph.link "vertex 3", "vertex 1"
95
+ @graph.link "vertex 2", "vertex 4"
96
+
97
+ graph2.link "vertex 1", "vertex 2"
98
+ graph2.link "vertex 2", "vertex 4"
99
+ graph2.link "vertex 3", "vertex 1"
100
+
101
+ (graph2 == @graph).must_equal false
102
+ (@graph == graph2).must_equal false
103
+ graph2.link "vertex 2", "vertex 1"
104
+ (graph2 == @graph).must_equal true
105
+ (@graph == graph2).must_equal true
106
+ end
107
+
108
+ it "is equal (==) to an array of edges" do
109
+ @graph.bilink "vertex 1", "vertex 2"
110
+ @graph.link "vertex 3", "vertex 1"
111
+ @graph.link "vertex 2", "vertex 4"
112
+ (@graph == [
113
+ [ "vertex 1", "vertex 2" ],
114
+ [ "vertex 2", "vertex 4" ],
115
+ [ "vertex 3", "vertex 1" ],
116
+ [ "vertex 2", "vertex 1" ]
117
+ ]).must_equal true
118
+ end
119
+
120
+ it "is only identical (equal?) to itself" do
121
+ graph2 = CognitiveDistance::Structures::Graph.new
122
+ @graph.link "a", "b"
123
+ graph2.link "a", "b"
124
+ (@graph == graph2).must_equal true
125
+ @graph.equal?(graph2).must_equal false
126
+ graph2.equal?(@graph).must_equal false
127
+ @graph.equal?(@graph).must_equal true
128
+ graph2.equal?(graph2).must_equal true
129
+ end
130
+
131
+ it "is type equal (eql?) to another graph iff they have the same edges" do
132
+ graph2 = TestSubGraph.new
133
+ graph3 = [ [ "a", "b" ] ]
134
+ @graph.link "a", "b"
135
+ graph2.link "a", "b"
136
+ @graph.eql?(graph2).must_equal true
137
+ graph2.eql?(@graph).must_equal true
138
+ @graph.eql?(graph3).must_equal false
139
+ graph2.eql?(graph3).must_equal false
140
+ end
141
+ end
142
+
@@ -0,0 +1,21 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ describe CognitiveDistance::Structures::MissingCallContext do
4
+ before do
5
+ @missing_call_context = CognitiveDistance::Structures::MissingCallContext.new
6
+ end
7
+
8
+ it "is not equal to some other object" do
9
+ @missing_call_context.equal?("test string").must_equal false
10
+ end
11
+
12
+ it "is not equal to another missing call context" do
13
+ other = CognitiveDistance::Structures::MissingCallContext.new
14
+ @missing_call_context.equal?(other).must_equal false
15
+ end
16
+
17
+ it "is not equal to itself" do
18
+ @missing_call_context.equal?(@missing_call_context).must_equal false
19
+ end
20
+ end
21
+
@@ -0,0 +1,57 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe CognitiveDistance::Tracer do
4
+ before do
5
+ @traced_obj = CognitiveDistance::Test::TracesInternal.new
6
+ @tracer = CognitiveDistance::Tracer.new(@traced_obj)
7
+ end
8
+
9
+ def to_methods arr
10
+ arr.map { |(node, children)|
11
+ [node.trace_method, to_methods(children)]
12
+ }
13
+ end
14
+
15
+ it "returns a CallTree after tracing" do
16
+ call_tree = @tracer.trace(:method1)
17
+ call_tree.is_a?(CognitiveDistance::Structures::CallTree).must_equal true
18
+ call_tree.to_a.size.must_equal 1
19
+ end
20
+
21
+ it "traces out a CallTree" do
22
+ call_tree = @tracer.trace(:method1)
23
+ to_methods(call_tree.to_a).must_equal [
24
+ [:method1, [
25
+ [:method2, [
26
+ [:method4, []]
27
+ ]],
28
+ [:method3, []]
29
+ ]]
30
+ ]
31
+ end
32
+
33
+ it "traces out properly when an exception is raised" do
34
+ call_tree = @tracer.trace(:test_raising)
35
+ to_methods(call_tree.to_a).must_equal [
36
+ [:test_raising, [
37
+ [:prepare_the_raising, [
38
+ [ :do_the_raising, [] ]
39
+ ]],
40
+ [:method3, []]
41
+ ]]
42
+ ]
43
+ end
44
+
45
+ it "traces out properly when throwing and catching" do
46
+ call_tree = @tracer.trace(:test_throwing)
47
+ to_methods(call_tree.to_a).must_equal [
48
+ [:test_throwing, [
49
+ [:prepare_the_throwing, [
50
+ [ :do_the_throwing, [] ]
51
+ ]],
52
+ [:method4, []]
53
+ ]]
54
+ ]
55
+ end
56
+ end
57
+