yardcheck 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +214 -0
  5. data/.rubocop_todo.yml +69 -0
  6. data/.ruby-version +1 -0
  7. data/Gemfile +22 -0
  8. data/Guardfile +5 -0
  9. data/README.md +83 -0
  10. data/bin/yardcheck +6 -0
  11. data/lib/yardcheck.rb +26 -0
  12. data/lib/yardcheck/color.rb +27 -0
  13. data/lib/yardcheck/const.rb +39 -0
  14. data/lib/yardcheck/documentation.rb +29 -0
  15. data/lib/yardcheck/documentation/method_object.rb +111 -0
  16. data/lib/yardcheck/method_call.rb +38 -0
  17. data/lib/yardcheck/method_tracer.rb +86 -0
  18. data/lib/yardcheck/observation.rb +72 -0
  19. data/lib/yardcheck/proxy.rb +33 -0
  20. data/lib/yardcheck/runner.rb +93 -0
  21. data/lib/yardcheck/source_lines.rb +29 -0
  22. data/lib/yardcheck/spec_observer.rb +28 -0
  23. data/lib/yardcheck/test_runner.rb +27 -0
  24. data/lib/yardcheck/test_value.rb +82 -0
  25. data/lib/yardcheck/typedef.rb +122 -0
  26. data/lib/yardcheck/typedef/parser.rb +82 -0
  27. data/lib/yardcheck/version.rb +5 -0
  28. data/lib/yardcheck/violation.rb +156 -0
  29. data/lib/yardcheck/warning.rb +14 -0
  30. data/spec/integration/yardcheck_spec.rb +57 -0
  31. data/spec/spec_helper.rb +51 -0
  32. data/spec/unit/yardcheck/const_spec.rb +48 -0
  33. data/spec/unit/yardcheck/documentation_spec.rb +148 -0
  34. data/spec/unit/yardcheck/method_tracer_spec.rb +59 -0
  35. data/spec/unit/yardcheck/runner_spec.rb +183 -0
  36. data/spec/unit/yardcheck/test_value_spec.rb +25 -0
  37. data/spec/unit/yardcheck/typedef/parser_spec.rb +37 -0
  38. data/spec/unit/yardcheck/typedef_spec.rb +32 -0
  39. data/test_app/.rspec +1 -0
  40. data/test_app/Gemfile +7 -0
  41. data/test_app/Gemfile.lock +66 -0
  42. data/test_app/lib/test_app.rb +119 -0
  43. data/test_app/spec/test_app_spec.rb +49 -0
  44. data/yardcheck.gemspec +24 -0
  45. metadata +158 -0
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Yardcheck::MethodTracer do
4
+ it 'traces method calls and returns for a namespace' do
5
+ stub_const('Foo', Class.new)
6
+ stub_const('Qux', Class.new)
7
+
8
+ class Foo
9
+ def self.singleton_method_example(baz)
10
+ baz.upcase
11
+ end
12
+
13
+ def instance_method_example(baz)
14
+ Foo.singleton_method_example(baz)
15
+ end
16
+ end # Foo
17
+
18
+ class Qux
19
+ def self.singleton_method_example(baz)
20
+ baz.upcase
21
+ end
22
+
23
+ def instance_method_example(baz)
24
+ Qux.singleton_method_example(baz)
25
+ end
26
+ end # Qux
27
+
28
+ tracer = described_class.new(Foo)
29
+ foo = Foo.new # Capture the activity for this object
30
+ qux = Qux.new # Ignore this one
31
+ str = 'Hello'
32
+
33
+ tracer.trace do
34
+ foo.instance_method_example(str)
35
+ qux.instance_method_example(str)
36
+ end
37
+
38
+ expect(tracer.events).to eq([
39
+ Yardcheck::MethodCall.process(
40
+ scope: :class,
41
+ selector: :singleton_method_example,
42
+ namespace: Foo.singleton_class,
43
+ params: { baz: 'Hello' },
44
+ return_value: 'HELLO',
45
+ example_location: RSpec.current_example.location,
46
+ error_raised: false
47
+ ),
48
+ Yardcheck::MethodCall.process(
49
+ scope: :instance,
50
+ selector: :instance_method_example,
51
+ namespace: Foo,
52
+ params: { baz: 'Hello' },
53
+ return_value: 'HELLO',
54
+ example_location: RSpec.current_example.location,
55
+ error_raised: false
56
+ )
57
+ ])
58
+ end
59
+ end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Yardcheck::Runner do
4
+ subject(:runner) do
5
+ described_class.new(observer.associate_with(docs), io)
6
+ end
7
+
8
+ let(:observer) { Yardcheck::SpecObserver.new(observed_events) }
9
+ let(:docs) { Yardcheck::Documentation.new(yardocs) }
10
+ let(:io) { StringIO.new }
11
+
12
+ let(:yardocs) do
13
+ YardcheckSpec::YARDOCS.each do |yardoc|
14
+ allow(yardoc).to receive(:file).and_wrap_original do |method|
15
+ File.join('./test_app', method.call).to_s
16
+ end
17
+ end
18
+ end
19
+
20
+ let(:observed_events) do
21
+ [
22
+ Yardcheck::MethodCall.process(
23
+ scope: :instance,
24
+ selector: :add,
25
+ namespace: TestApp::Namespace,
26
+ params: { left: 'foo', right: 3 },
27
+ return_value: 5,
28
+ example_location: 'test_app_spec.rb:1',
29
+ error_raised: false
30
+ ),
31
+ Yardcheck::MethodCall.process(
32
+ scope: :class,
33
+ selector: :add,
34
+ namespace: TestApp::Namespace.singleton_class,
35
+ params: { left: 2, right: 3 },
36
+ return_value: 5,
37
+ example_location: 'test_app_spec.rb:2',
38
+ error_raised: false
39
+ )
40
+ ]
41
+ end
42
+
43
+ let(:output) do
44
+ <<~MSG
45
+ Expected #<Class:TestApp::Namespace>#add to return String but observed Fixnum
46
+
47
+ source: ./test_app/lib/test_app.rb:9
48
+ tests:
49
+ - test_app_spec.rb:2
50
+
51
+ # Singleton method with correct param definition and incorrect return
52
+ #
53
+ # @param left [Integer]
54
+ # @param right [Integer]
55
+ #
56
+ # @return [String]
57
+ def self.add(left, right)
58
+ left + right
59
+ end
60
+
61
+ Expected TestApp::Namespace#add to receive Integer for left but observed String
62
+
63
+ source: ./test_app/lib/test_app.rb:19
64
+ tests:
65
+ - test_app_spec.rb:1
66
+
67
+ # Instance method with correct param definition and incorrect return
68
+ #
69
+ # @param left [Integer]
70
+ # @param right [Integer]
71
+ #
72
+ # @return [String]
73
+ def add(left, right)
74
+ left + right
75
+ end
76
+
77
+ Expected TestApp::Namespace#add to return String but observed Fixnum
78
+
79
+ source: ./test_app/lib/test_app.rb:19
80
+ tests:
81
+ - test_app_spec.rb:1
82
+
83
+ # Instance method with correct param definition and incorrect return
84
+ #
85
+ # @param left [Integer]
86
+ # @param right [Integer]
87
+ #
88
+ # @return [String]
89
+ def add(left, right)
90
+ left + right
91
+ end
92
+
93
+ MSG
94
+ end
95
+
96
+ def remove_color(string)
97
+ string.gsub(/\e\[(?:1\;)?\d+m/, '')
98
+ end
99
+
100
+ shared_examples 'violation output' do
101
+ it 'compares the spec observations against the documentation' do
102
+ runner.check
103
+
104
+ expect(remove_color(io.string)).to eql(output)
105
+ end
106
+ end
107
+
108
+ include_examples 'violation output'
109
+
110
+ context 'when multiple tests find the same violation' do
111
+ let(:observed_events) do
112
+ [
113
+ Yardcheck::MethodCall.process(
114
+ scope: :instance,
115
+ selector: :add,
116
+ namespace: TestApp::Namespace,
117
+ params: { left: 'foo', right: 3 },
118
+ return_value: 'valid return type',
119
+ example_location: 'test_app_spec.rb:1',
120
+ error_raised: false
121
+ ),
122
+ Yardcheck::MethodCall.process(
123
+ scope: :instance,
124
+ selector: :add,
125
+ namespace: TestApp::Namespace,
126
+ params: { left: 'foo', right: 3 },
127
+ return_value: 'valid return type',
128
+ example_location: 'test_app_spec.rb:2',
129
+ error_raised: false
130
+ ),
131
+ Yardcheck::MethodCall.process(
132
+ scope: :instance,
133
+ selector: :add,
134
+ namespace: TestApp::Namespace,
135
+ params: { left: 1, right: 'now this one is wrong' },
136
+ return_value: 'valid return type',
137
+ example_location: 'test_app_spec.rb:3',
138
+ error_raised: false
139
+ )
140
+ ]
141
+ end
142
+
143
+ let(:output) do
144
+ <<~MSG
145
+ Expected TestApp::Namespace#add to receive Integer for left but observed String
146
+
147
+ source: ./test_app/lib/test_app.rb:19
148
+ tests:
149
+ - test_app_spec.rb:1
150
+ - test_app_spec.rb:2
151
+
152
+ # Instance method with correct param definition and incorrect return
153
+ #
154
+ # @param left [Integer]
155
+ # @param right [Integer]
156
+ #
157
+ # @return [String]
158
+ def add(left, right)
159
+ left + right
160
+ end
161
+
162
+ Expected TestApp::Namespace#add to receive Integer for right but observed String
163
+
164
+ source: ./test_app/lib/test_app.rb:19
165
+ tests:
166
+ - test_app_spec.rb:3
167
+
168
+ # Instance method with correct param definition and incorrect return
169
+ #
170
+ # @param left [Integer]
171
+ # @param right [Integer]
172
+ #
173
+ # @return [String]
174
+ def add(left, right)
175
+ left + right
176
+ end
177
+
178
+ MSG
179
+ end
180
+
181
+ include_examples 'violation output'
182
+ end
183
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Yardcheck::TestValue do
4
+ it 'just wraps normal values' do
5
+ expect(described_class.process(1)).to eql(described_class.new(1))
6
+ end
7
+
8
+ it 'extracts the namespace from instance doubles' do
9
+ expect(described_class.process(instance_double(String))).to eql(
10
+ described_class::InstanceDouble.new(String)
11
+ )
12
+ end
13
+
14
+ it 'extracts the name from doubles' do
15
+ expect(described_class.process(double(:foo))).to eql(
16
+ described_class::Double.new(:foo)
17
+ )
18
+ end
19
+
20
+ it 'handles anonymous doubles' do
21
+ expect(described_class.process(double)).to eql(
22
+ described_class::Double.new('(anonymous)')
23
+ )
24
+ end
25
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Yardcheck::Typedef::Parser do
4
+ def parse(*types)
5
+ described_class.new('TestApp::Namespace', types).parse
6
+ end
7
+
8
+ it 'resolves one normal constant' do
9
+ expect(parse('Integer')).to eql(Yardcheck::Typedef.new([
10
+ Yardcheck::Typedef::Literal.new(Yardcheck::Const.new(Integer))
11
+ ]))
12
+ end
13
+
14
+ it 'resolves multiple constants' do
15
+ expect(parse('Integer', 'String')).to eql(Yardcheck::Typedef.new([
16
+ Yardcheck::Typedef::Literal.new(Yardcheck::Const.new(Integer)),
17
+ Yardcheck::Typedef::Literal.new(Yardcheck::Const.new(String))
18
+ ]))
19
+ end
20
+
21
+ it 'resolves child of namespace' do
22
+ expect(parse('Child')).to eql(Yardcheck::Typedef.new([
23
+ Yardcheck::Typedef::Literal.new(Yardcheck::Const.new(TestApp::Namespace::Child))
24
+ ]))
25
+ end
26
+
27
+ it 'handles array of items' do
28
+ expect(parse('Array<String>')).to eql(
29
+ Yardcheck::Typedef.new([
30
+ Yardcheck::Typedef::Collection.new(
31
+ Yardcheck::Const.new(Array),
32
+ [Yardcheck::Typedef::Literal.new(Yardcheck::Const.new(String))]
33
+ )
34
+ ])
35
+ )
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Yardcheck::Typedef do
4
+ def typedef(*types)
5
+ typedefs =
6
+ types.map do |type|
7
+ described_class::Literal.new(Yardcheck::Const.new(type))
8
+ end
9
+
10
+ described_class.new(typedefs)
11
+ end
12
+
13
+ it 'matches exact type matches' do
14
+ expect(typedef(Integer).match?(Yardcheck::TestValue.new(1))).to be(true)
15
+ end
16
+
17
+ it 'matches descendants' do
18
+ parent = Class.new
19
+ child = Class.new(parent)
20
+
21
+ expect(typedef(parent).match?(Yardcheck::TestValue.new(child.new))).to be(true)
22
+ end
23
+
24
+ it 'matches union type definitions' do
25
+ aggregate_failures do
26
+ definition = typedef(Integer, String)
27
+ expect(definition.match?(Yardcheck::TestValue.new(1))).to be(true)
28
+ expect(definition.match?(Yardcheck::TestValue.new('hi'))).to be(true)
29
+ expect(definition.match?(Yardcheck::TestValue.new(:hi))).to be(false)
30
+ end
31
+ end
32
+ end
data/test_app/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/test_app/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'yardcheck', path: '..'
6
+ gem 'pry'
7
+ gem 'pry-byebug'
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ yardcheck (0.0.0)
5
+ anima
6
+ coderay (~> 1.1)
7
+ concord
8
+ rspec
9
+ yard (~> 0.9)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ abstract_type (0.0.7)
15
+ adamantium (0.2.0)
16
+ ice_nine (~> 0.11.0)
17
+ memoizable (~> 0.4.0)
18
+ anima (0.3.0)
19
+ abstract_type (~> 0.0.7)
20
+ adamantium (~> 0.2)
21
+ equalizer (~> 0.0.11)
22
+ byebug (9.0.6)
23
+ coderay (1.1.1)
24
+ concord (0.1.5)
25
+ adamantium (~> 0.2.0)
26
+ equalizer (~> 0.0.9)
27
+ diff-lcs (1.2.5)
28
+ equalizer (0.0.11)
29
+ ice_nine (0.11.2)
30
+ memoizable (0.4.2)
31
+ thread_safe (~> 0.3, >= 0.3.1)
32
+ method_source (0.8.2)
33
+ pry (0.10.4)
34
+ coderay (~> 1.1.0)
35
+ method_source (~> 0.8.1)
36
+ slop (~> 3.4)
37
+ pry-byebug (3.4.2)
38
+ byebug (~> 9.0)
39
+ pry (~> 0.10)
40
+ rspec (3.5.0)
41
+ rspec-core (~> 3.5.0)
42
+ rspec-expectations (~> 3.5.0)
43
+ rspec-mocks (~> 3.5.0)
44
+ rspec-core (3.5.4)
45
+ rspec-support (~> 3.5.0)
46
+ rspec-expectations (3.5.0)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.5.0)
49
+ rspec-mocks (3.5.0)
50
+ diff-lcs (>= 1.2.0, < 2.0)
51
+ rspec-support (~> 3.5.0)
52
+ rspec-support (3.5.0)
53
+ slop (3.6.0)
54
+ thread_safe (0.3.6)
55
+ yard (0.9.8)
56
+
57
+ PLATFORMS
58
+ ruby
59
+
60
+ DEPENDENCIES
61
+ pry
62
+ pry-byebug
63
+ yardcheck!
64
+
65
+ BUNDLED WITH
66
+ 1.14.6
@@ -0,0 +1,119 @@
1
+ module TestApp
2
+ class Namespace
3
+ # Singleton method with correct param definition and incorrect return
4
+ #
5
+ # @param left [Integer]
6
+ # @param right [Integer]
7
+ #
8
+ # @return [String]
9
+ def self.add(left, right)
10
+ left + right
11
+ end
12
+
13
+ # Instance method with correct param definition and incorrect return
14
+ #
15
+ # @param left [Integer]
16
+ # @param right [Integer]
17
+ #
18
+ # @return [String]
19
+ def add(left, right)
20
+ left + right
21
+ end
22
+
23
+ # Untested method with documentation
24
+ #
25
+ # @param str [String]
26
+ #
27
+ # @return [String]
28
+ def untested_method(str)
29
+ end
30
+
31
+ def undocumented
32
+ end
33
+
34
+ # @param foo [What]
35
+ #
36
+ # @return [Wow]
37
+ def ignoring_invalid_types(foo)
38
+ end
39
+
40
+ # @return [TestApp::Namespace::Parent]
41
+ def returns_generic
42
+ Child.new
43
+ end
44
+
45
+ # @return [Child]
46
+ def documents_relative
47
+ 'str'
48
+ end
49
+
50
+ # @return no type specified here
51
+ def return_tag_without_type
52
+ end
53
+
54
+ # @param [String]
55
+ def param_without_name(unnamed)
56
+ end
57
+
58
+ # @return [nil]
59
+ def return_nil
60
+ end
61
+
62
+ # @return [Namespace]
63
+ def return_self
64
+ self
65
+ end
66
+
67
+ # @return [undefined]
68
+ def undefined_return
69
+ end
70
+
71
+ # @return [Boolean]
72
+ def bool_return
73
+ end
74
+
75
+ # @return [Array<String>]
76
+ def array_return
77
+ end
78
+
79
+ # @return [String] in some cases
80
+ # @return [nil] otherwise
81
+ def multiple_returns
82
+ end
83
+
84
+ # @param list [Enumerable<Integer>]
85
+ # @return [nil]
86
+ def enumerable_param(list)
87
+ end
88
+
89
+ # @param value [String]
90
+ # @return [nil]
91
+ def properly_tested_with_instance_double(value)
92
+ end
93
+
94
+ # @param value [String]
95
+ # @return [nil]
96
+ def improperly_tested_with_instance_double(value)
97
+ end
98
+
99
+ AppError = Class.new(StandardError)
100
+
101
+ # @return [Fixnum]
102
+ def always_raise
103
+ raise AppError
104
+
105
+ 1
106
+ end
107
+
108
+ # @return [:foo]
109
+ def returns_literal_symbol
110
+ :foo
111
+ end
112
+
113
+ class Parent
114
+ end
115
+
116
+ class Child < Parent
117
+ end
118
+ end
119
+ end