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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.rubocop.yml +214 -0
- data/.rubocop_todo.yml +69 -0
- data/.ruby-version +1 -0
- data/Gemfile +22 -0
- data/Guardfile +5 -0
- data/README.md +83 -0
- data/bin/yardcheck +6 -0
- data/lib/yardcheck.rb +26 -0
- data/lib/yardcheck/color.rb +27 -0
- data/lib/yardcheck/const.rb +39 -0
- data/lib/yardcheck/documentation.rb +29 -0
- data/lib/yardcheck/documentation/method_object.rb +111 -0
- data/lib/yardcheck/method_call.rb +38 -0
- data/lib/yardcheck/method_tracer.rb +86 -0
- data/lib/yardcheck/observation.rb +72 -0
- data/lib/yardcheck/proxy.rb +33 -0
- data/lib/yardcheck/runner.rb +93 -0
- data/lib/yardcheck/source_lines.rb +29 -0
- data/lib/yardcheck/spec_observer.rb +28 -0
- data/lib/yardcheck/test_runner.rb +27 -0
- data/lib/yardcheck/test_value.rb +82 -0
- data/lib/yardcheck/typedef.rb +122 -0
- data/lib/yardcheck/typedef/parser.rb +82 -0
- data/lib/yardcheck/version.rb +5 -0
- data/lib/yardcheck/violation.rb +156 -0
- data/lib/yardcheck/warning.rb +14 -0
- data/spec/integration/yardcheck_spec.rb +57 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/unit/yardcheck/const_spec.rb +48 -0
- data/spec/unit/yardcheck/documentation_spec.rb +148 -0
- data/spec/unit/yardcheck/method_tracer_spec.rb +59 -0
- data/spec/unit/yardcheck/runner_spec.rb +183 -0
- data/spec/unit/yardcheck/test_value_spec.rb +25 -0
- data/spec/unit/yardcheck/typedef/parser_spec.rb +37 -0
- data/spec/unit/yardcheck/typedef_spec.rb +32 -0
- data/test_app/.rspec +1 -0
- data/test_app/Gemfile +7 -0
- data/test_app/Gemfile.lock +66 -0
- data/test_app/lib/test_app.rb +119 -0
- data/test_app/spec/test_app_spec.rb +49 -0
- data/yardcheck.gemspec +24 -0
- 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,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
|