[ruby/uri] Also warn URI::RFC3986_PARSER.extract
[ruby.git] / spec / ruby / CONTRIBUTING.md
blobe9314208d10d7aa4d2158b13ee26942d22245a5e
1 Contributions are much appreciated.
2 Please open a pull request or add an issue to discuss what you intend to work on.
3 If the pull requests passes the CI and conforms to the existing style of specs, it will be merged.
5 ### File organization
7 Spec are grouped in 5 separate top-level groups:
9 * `command_line`: for the ruby executable command-line flags (`-v`, `-e`, etc)
10 * `language`: for the language keywords and syntax constructs (`if`, `def`, `A::B`, etc)
11 * `core`: for the core methods (`Integer#+`, `String#upcase`, no need to require anything)
12 * `library`: for the standard libraries methods (`CSV.new`, `YAML.parse`, need to require the stdlib)
13 * `optional/capi`: for functions available to the Ruby C-extension API
15 The exact file for methods is decided by the `#owner` of a method, for instance for `#group_by`:
17 ```ruby
18 > [].method(:group_by)
19 => #<Method: Array(Enumerable)#group_by>
20 > [].method(:group_by).owner
21 => Enumerable
22 ```
24 Which should therefore be specified in `core/enumerable/group_by_spec.rb`.
26 ### MkSpec - a tool to generate the spec structure
28 If you want to create new specs, you should use `mkspec`, part of [MSpec](http://github.com/ruby/mspec).
30     $ ../mspec/bin/mkspec -h
32 #### Creating files for unspecified modules or classes
34 For instance, to create specs for `forwardable`:
36     $ ../mspec/bin/mkspec -b library -rforwardable -c Forwardable
38 Specify `core` or `library` as the `base`.
40 #### Finding unspecified core methods
42 This is very easy, just run the command below in your `spec` directory.
43 `ruby` must be a recent version of MRI.
45     $ ruby --disable-gem ../mspec/bin/mkspec
47 You might also want to search for:
49     it "needs to be reviewed for spec completeness"
51 which indicates the file was generated but the method unspecified.
53 ### Matchers and expectations
55 Here is a list of frequently-used matchers, which should be enough for most specs.
56 There are a few extra specific matchers used in the couple specs that need it.
58 #### Comparison matchers
60 ```ruby
61 (1 + 2).should == 3 # Calls #==
62 (1 + 2).should_not == 5
64 File.should.equal?(File) # Calls #equal? (tests identity)
65 (1 + 2).should.eql?(3) # Calls #eql? (Hash equality)
67 1.should < 2
68 2.should <= 2
69 3.should >= 3
70 4.should > 3
72 "Hello".should =~ /l{2}/ # Calls #=~ (Regexp match)
73 ```
75 #### Predicate matchers
77 ```ruby
78 [].should.empty?
79 [1,2,3].should.include?(2)
81 "hello".should.start_with?("h")
82 "hello".should.end_with?("o")
84 (0.1 + 0.2).should be_close(0.3, TOLERANCE) # (0.2-0.1).abs < TOLERANCE
85 (0.0/0.0).should.nan?
86 (1.0/0.0).should be_positive_infinity
87 (-1.0/0.0).should be_negative_infinity
89 3.14.should be_an_instance_of(Float) # Calls #instance_of?
90 3.14.should be_kind_of(Numeric) # Calls #is_a?
91 Numeric.should be_ancestor_of(Float) # Float.ancestors.include?(Numeric)
93 3.14.should.respond_to?(:to_i)
94 Integer.should have_instance_method(:+)
95 Array.should have_method(:new)
96 ```
98 Also `have_constant`, `have_private_instance_method`, `have_singleton_method`, etc.
100 #### Exception matchers
102 ```ruby
103 -> {
104   raise "oops"
105 }.should raise_error(RuntimeError, /oops/)
107 -> {
108   raise "oops"
109 }.should raise_error(RuntimeError) { |e|
110   # Custom checks on the Exception object
111   e.message.should.include?("oops")
112   e.cause.should == nil
116 ##### should_not raise_error
118 **To avoid!** Instead, use an expectation testing what the code in the lambda does.
119 If an exception is raised, it will fail the example anyway.
121 ```ruby
122 -> { ... }.should_not raise_error
125 #### Warning matcher
127 ```ruby
128 -> {
129   Fixnum
130 }.should complain(/constant ::Fixnum is deprecated/) # Expect a warning
133 ### Guards
135 Different guards are available as defined by mspec.
136 Here is a list of the most commonly-used guards:
138 #### Version guards
140 ```ruby
141 ruby_version_is ""..."3.2" do
142   # Specs for RUBY_VERSION < 3.2
145 ruby_version_is "3.2" do
146   # Specs for RUBY_VERSION >= 3.2
150 #### Platform guards
152 ```ruby
153 platform_is :windows do
154   # Specs only valid on Windows
157 platform_is_not :windows do
158   # Specs valid on platforms other than Windows
161 platform_is :linux, :darwin do # OR
164 platform_is_not :linux, :darwin do # Not Linux and not Darwin
167 platform_is pointer_size: 64 do
168   # 64-bit platform
171 big_endian do
172   # Big-endian platform
176 #### Guard for bug
178 In case there is a bug in MRI and the fix will be backported to previous versions.
179 If it is not backported or not likely, use `ruby_version_is` instead.
180 First, file a bug at https://bugs.ruby-lang.org/.
181 The problem is `ruby_bug` would make non-MRI implementations fail this spec while MRI itself does not pass it, so it should only be used if the bug is/will be fixed and backported.
182 Otherwise, non-MRI implementations would have to choose between being incompatible with the latest release of MRI to pass the spec or fail the spec, both which make no sense.
184 ```ruby
185 ruby_bug '#13669', ''...'3.2' do
186   it "works like this" do
187     # Specify the expected behavior here, not the bug
188   end
192 #### Combining guards
194 ```ruby
195 guard -> { platform_is :windows and ruby_version_is ""..."3.2" } do
196   # Windows and RUBY_VERSION < 3.2
199 guard_not -> { platform_is :windows and ruby_version_is ""..."3.2" } do
200   # The opposite
204 #### Custom guard
206 ```ruby
207 max_uint = (1 << 32) - 1
208 guard -> { max_uint <= fixnum_max } do
212 Custom guards are better than a simple `if` as they allow [mspec commands](https://github.com/ruby/mspec/issues/30#issuecomment-312487779) to work properly.
214 #### Implementation-specific behaviors
216 In general, the usage of guards should be minimized as possible.
218 There are no guards to define implementation-specific behavior because
219 the Ruby Spec Suite defines common behavior and not implementation details.
220 Use the implementation test suite for these.
222 If an implementation does not support some feature, simply tag the related specs as failing instead.
224 ### Shared Specs
226 Often throughout Ruby, identical functionality is used by different methods and modules. In order
227 to avoid duplication of specs, we have shared specs that are re-used in other specs. The use is a
228 bit tricky however, so let's go over it.
230 Commonly, if a shared spec is only reused within its own module, the shared spec will live within a
231 shared directory inside that module's directory. For example, the `core/hash/shared/key.rb` spec is
232 only used by `Hash` specs, and so it lives inside `core/hash/shared/`.
234 When a shared spec is used across multiple modules or classes, it lives within the `shared/` directory.
235 An example of this is the `shared/file/socket.rb` which is used by `core/file/socket_spec.rb`,
236 `core/filetest/socket_spec.rb`, and `core/file/state/socket_spec.rb` and so it lives in the root `shared/`.
238 Defining a shared spec involves adding a `shared: true` option to the top-level `describe` block. This
239 will signal not to run the specs directly by the runner. Shared specs have access to two instance
240 variables from the implementor spec: `@method` and `@object`, which the implementor spec will pass in.
242 Here's an example of a snippet of a shared spec and two specs which integrates it:
244 ```ruby
245 # core/hash/shared/key.rb
246 describe :hash_key_p, shared: true do
247   it "returns true if the key's matching value was false" do
248     { xyz: false }.send(@method, :xyz).should == true
249   end
252 # core/hash/key_spec.rb
253 describe "Hash#key?" do
254   it_behaves_like :hash_key_p, :key?
257 # core/hash/include_spec.rb
258 describe "Hash#include?" do
259   it_behaves_like :hash_key_p, :include?
263 In the example, the first `describe` defines the shared spec `:hash_key_p`, which defines a spec that
264 calls the `@method` method with an expectation. In the implementor spec, we use `it_behaves_like` to
265 integrate the shared spec. `it_behaves_like` takes 3 parameters: the key of the shared spec, a method,
266 and an object. These last two parameters are accessible via `@method` and `@object` in the shared spec.
268 Sometimes, shared specs require more context from the implementor class than a simple object. We can address
269 this by passing a lambda as the method, which will have the scope of the implementor. Here's an example of
270 how this is used currently:
272 ```ruby
273 describe :kernel_sprintf, shared: true do
274   it "raises TypeError exception if cannot convert to Integer" do
275     -> { @method.call("%b", Object.new) }.should raise_error(TypeError)
276   end
279 describe "Kernel#sprintf" do
280   it_behaves_like :kernel_sprintf, -> format, *args {
281     sprintf(format, *args)
282   }
285 describe "Kernel.sprintf" do
286   it_behaves_like :kernel_sprintf, -> format, *args {
287     Kernel.sprintf(format, *args)
288   }
292 In the above example, the method being passed is a lambda that triggers the specific conditions of the shared spec.
294 ### Style
296 Do not leave any trailing space and follow the existing style.