yardcheck 0.0.1

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 (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