Guards

Version 20 (Brian Ford, 02/06/2009 03:11 PM)

1 1
h1. Guards
2 1
3 1
h2. Overview
4 1
5 9 Brian Ford
The RubySpec project intends to provide a complete and exhaustive specification of the Ruby language and its libraries.
6 9 Brian Ford
There is a single _standard_ implementation of Ruby. The _standard_ includes the stable, released versions available from http://ruby-lang.org. At present, the _standard_ is 1.8.6, 1.8.7, and 1.9.1. Collectively, the _standard_ is often referred to as MatzRuby or MRI.
7 9 Brian Ford
8 9 Brian Ford
The challenge for RubySpec is to correctly spec the different behaviors across versions, platforms, and implementations. To do this, the RubySpecs depend on _guards_ provided by MSpec. Guards are methods that may or may not take arguments and operate by yielding to a block if certain conditions are true. If the conditions for the guard are not true, the guard method does not yield to the block and the specs contained in the block are not run.
9 9 Brian Ford
10 11 Brian Ford
The guards serve two functions: 1) controlling which specs are run; 2) documenting the specs. The documentation function of the guards is as important for RubySpec as controlling which specs are run. Additionally, the guard structure itself was chosen to be visually and conceptually similar to the describe/it blocks in the specs.
11 1
12 12 Brian Ford
The guard blocks should be placed around @describe@ or @it@ blocks. Guards should _not_ be placed inside @it@ blocks. Guards should rarely, if ever, be placed inside @before@ or @after@ actions.
13 12 Brian Ford
14 11 Brian Ford
There are five categories of guards:
15 11 Brian Ford
16 9 Brian Ford
# versions
17 9 Brian Ford
# platforms
18 9 Brian Ford
# bugs
19 9 Brian Ford
# implementations
20 9 Brian Ford
# environments
21 9 Brian Ford
22 9 Brian Ford
The specific guards in each of these categories are explained below.
23 9 Brian Ford
24 9 Brian Ford
h3. 1. Versions
25 9 Brian Ford
26 9 Brian Ford
Different versions of Ruby have same methods that behave differently. That sounds circular, but it is the essence of software versions. To handle different version behaviors, MSpec provides the ruby_version_is guard.
27 9 Brian Ford
28 9 Brian Ford
29 9 Brian Ford
ruby_version_is "1.8.6.114" do
30 9 Brian Ford
  it "returns true" do
31 9 Brian Ford
  end
32 9 Brian Ford
end
33 9 Brian Ford
34 9 Brian Ford
ruby_version_is "1.8" .. "1.8.6" do
35 9 Brian Ford
  it "returns nil" do
36 9 Brian Ford
  end
37 9 Brian Ford
end
38 9 Brian Ford
39 9 Brian Ford
ruby_version_is "" ... "1.9" do
40 9 Brian Ford
  it "returns false" do
41 9 Brian Ford
  end
42 9 Brian Ford
end
43 9 Brian Ford
44 9 Brian Ford
45 10 Brian Ford
The ruby_version_is guard takes one argument that may be a string or a range of two strings. The format of the string is A.B.C.D, where:
46 10 Brian Ford
47 10 Brian Ford
* A is the major version
48 10 Brian Ford
* B is the minor version
49 10 Brian Ford
* C is the tiny (teeny) version
50 10 Brian Ford
* D is the patchlevel
51 10 Brian Ford
52 10 Brian Ford
The string is converted to a number on which comparisons between versions can be made so that, for instance, "1.8.6.37" is less than "1.8.6.112".
53 10 Brian Ford
54 10 Brian Ford
The range behaves as expected, respecting #exclude_end?. In the example above, "" ... "1.9" means every version *before* 1.9.
55 10 Brian Ford
56 9 Brian Ford
h3. 2. Platforms
57 1
58 11 Brian Ford
A single version of Ruby may have different behaviors depending on the platform on which it runs. MSpec provides several guards for these situations:
59 11 Brian Ford
60 11 Brian Ford
# big_endian
61 11 Brian Ford
# little_endian
62 11 Brian Ford
# platform_is
63 11 Brian Ford
# platform_is_not
64 9 Brian Ford
65 13 Brian Ford
The @big_endian@ guard yields to the block if the platform is big endian. Likewise for the @little_endian@ guard.
66 13 Brian Ford
67 13 Brian Ford
The platform_is and platform_is_not guards are more complex. As their names suggest, they are inverses.
68 13 Brian Ford
69 13 Brian Ford
70 13 Brian Ford
platform_is :linux, :bsd do
71 13 Brian Ford
  it "opens the file" do
72 13 Brian Ford
  end
73 13 Brian Ford
end
74 13 Brian Ford
75 13 Brian Ford
76 13 Brian Ford
The guard above will yield if RUBY_PLATFORM matches either "linux" OR "bsd".
77 13 Brian Ford
78 13 Brian Ford
79 13 Brian Ford
platform_is :linux, :wordsize => 32 do
80 13 Brian Ford
  it "opens the file" do
81 13 Brian Ford
  end
82 13 Brian Ford
end
83 13 Brian Ford
84 13 Brian Ford
85 13 Brian Ford
The guard above will yield if RUBY_PLATFORM matches "linux" AND the processor word size is 32-bit.
86 13 Brian Ford
87 13 Brian Ford
88 13 Brian Ford
platform_is_not :windows, :wordsize => 32 do
89 13 Brian Ford
  it "opens the file" do
90 13 Brian Ford
  end
91 13 Brian Ford
end
92 13 Brian Ford
93 13 Brian Ford
94 13 Brian Ford
The guard above will yield if RUBY_PLATFORM does not matches "windows" AND the processor word size is not 32-bit.
95 13 Brian Ford
96 13 Brian Ford
Special functionality exists for matching :windows and :java as platforms. For details, refer to the MSpec source.
97 13 Brian Ford
98 13 Brian Ford
99 14 Brian Ford
platform_is :os => [:darwin, :bsd] do
100 13 Brian Ford
  it "opens the file" do
101 13 Brian Ford
  end
102 13 Brian Ford
end
103 13 Brian Ford
104 13 Brian Ford
105 13 Brian Ford
The guard above will yield if Config::CONFIG['host_os'] matches either "darwin" OR "BSD". If Config::CONFIG['host_os'] is not set in rbconfig, the :os parameters will be matched against RUBY_PLATFORM instead.
106 13 Brian Ford
107 9 Brian Ford
h3. 3. Bugs
108 1
109 15 Brian Ford
Sometimes a bug is discovered in the _standard_. In this case, we do two things:
110 1
111 15 Brian Ford
# File a ticket on the "bug tracker":http://redmine.ruby-lang.org/ to find out if the suspected behavior is actually considered a bug.
112 15 Brian Ford
# Add a @ruby_bug@ guard that wraps the spec showing what is considered to be the _correct_ behavior.
113 2 Eero Saynatkari
114 15 Brian Ford
115 15 Brian Ford
ruby_bug "#5555", "1.8.6.114" do
116 15 Brian Ford
  it "returns the sum" do
117 15 Brian Ford
    (1 + 1).should == 2
118 7 Federico Builes
  end
119 6 Federico Builes
end
120 15 Brian Ford
121 6 Federico Builes
122 15 Brian Ford
The @ruby_bug@ guard serves three purposes:
123 6 Federico Builes
124 15 Brian Ford
# It documents that there is a bug in a particular version of the standard and refers to the ticket for that bug.
125 15 Brian Ford
# It provides the correct behavior description for all implementations
126 15 Brian Ford
# It prevents the spec for the correct behavior from running (and failing) on versions of the _standard_ implementation that have the bug and have already been released.
127 6 Federico Builes
128 15 Brian Ford
h3. 4. Implementations
129 2 Eero Saynatkari
130 19 Brian Ford
Ideally, all Ruby implementations would have exactly the same behaviors. This is not realistically possible given differences in the underlying technology, for instance, whether the process @fork@ facility is available.
131 19 Brian Ford
132 19 Brian Ford
It should be obvious, but it bears repeating, that in a specification for a standard, there should be an absolute minimum of incompatible behavior. These guards are not provided to make it easy to be inconsistent. Rather, they are provided to make specifying a single standard as simple as possible, recognizing that 100% conformity is an impossible ideal.
133 19 Brian Ford
134 19 Brian Ford
Recall that the purpose of the guards are to _both_ control which specs are run AND document the specs. There are four distinct situations covered by the five guards below. Each of the guards documents this difference.
135 19 Brian Ford
136 16 Brian Ford
# compliant_on
137 16 Brian Ford
# not_compliant_on
138 16 Brian Ford
# not_supported_on
139 16 Brian Ford
# deviates_on
140 16 Brian Ford
# extended_on
141 2 Eero Saynatkari
142 20 Brian Ford
The @compliant_on@ and not_compliant_on guards are inverses. They document that the enclosed spec passes or does not pass on the listed implementations or platforms.
143 19 Brian Ford
144 20 Brian Ford
145 20 Brian Ford
compliant_on :jruby, :rubinius do
146 20 Brian Ford
  it "returns true" do
147 20 Brian Ford
  end
148 20 Brian Ford
end
149 20 Brian Ford
150 15 Brian Ford
151 20 Brian Ford
The spec above will ONLY run on the _standard_, JRuby, or Rubinius. The @compliant_on@ guard will not run on any other implementation or platform except the ones listed.
152 20 Brian Ford
153 20 Brian Ford
There is no need to list :ruby or :ruby19 for this guard. There is only one standard and specs are _always_ expected run correctly on the _standard_. If there is version-specific behavior or a bug in the _standard_, use the @ruby_bug@ or ruby_version_is guard instead.
154 20 Brian Ford
155 20 Brian Ford
156 20 Brian Ford
not_compliant_on :rubinius do
157 20 Brian Ford
  it "returns false" do
158 20 Brian Ford
  end
159 20 Brian Ford
end
160 20 Brian Ford
161 20 Brian Ford
162 20 Brian Ford
The spec above will run on EVERY implementation except Rubinius.
163 20 Brian Ford
164 20 Brian Ford
It should be obvious that the @compliant_on@ guard is more convenient when the number of implementations that conform to the standard is small compared to the total number of implementations. The not_compliant_on guard is useful when the number of non-conforming implementations is small.
165 20 Brian Ford
166 16 Brian Ford
h3. 5. Environments
167 18 Brian Ford
168 16 Brian Ford
The following guards are broadly grouped by their relation to how the specs are run. This is referred to as the spec _environment_. It includes guards for which runner (e.g. RSpec or MSpec) is executing the specs, which classes are loaded when the specs run, and whether the process running the specs has superuser privileges.
169 16 Brian Ford
170 16 Brian Ford
# runner_is
171 16 Brian Ford
# runner_is_not
172 1
# conflicts_with
173 20 Brian Ford
# as_superuser
174 17 Brian Ford
# quarantine!
175 1
176 19 Brian Ford
The quarantine! guard will never yield to the block, so the specs inside the guard will not run. This guard is only used to temporarily disable a guard that is causing the _standard_ to fail severely (for example, by causing a segfault) AND the correctness of the spec is suspect. The guard allows the spec to be investigated but not cause any failures.
177 17 Brian Ford
178 17 Brian Ford
If a spec exposes a bug that is causing a segfault, the @ruby_bug@ guard should be used.
179 1
180 18 Brian Ford
181 17 Brian Ford
quarantine! do 
182 17 Brian Ford
  it "does something that causes a segfault" do
183 1
  end
184 17 Brian Ford
end
185 18 Brian Ford