cognitive_distance 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
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
+